{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "227905c3-4de3-4717-847c-cb53cd331e5e",
   "metadata": {},
   "source": [
    "# Python中异常处理\n",
    "\n",
    "### 第一节：了解错误和异常\n",
    "\n",
    "#### 1. 错误与异常的概念\n",
    "\n",
    "- **错误（Error）**通常指的是编译时或者解析时出现的问题，比如语法错误，它们会阻止代码的运行。\n",
    "- **异常（Exception）**则是指在程序执行过程中发生的事件，这些事件会打断正常的程序流程。\n",
    "\n",
    "#### 2. 错误与异常的区别\n",
    "\n",
    "- 错误通常是无法通过程序本身来处理的问题，而异常则可以被程序捕获并进行处理。不可预期\n",
    "- 错误往往是更严重的问题，可能需要修改代码来解决；异常则可能是由于外部条件引起的，通过异常处理机制可以使程序更加健壮。可预期\n",
    "\n",
    "#### 3. Python中常见的错误类型\n",
    "\n",
    "- **SyntaxError（语法错误）**：当Python解释器遇到语法上的错误时抛出。\n",
    "- **IndentationError（缩进错误）**：当代码的缩进不符合语法规定时抛出。\n",
    "\n",
    "#### 4. 异常处理的重要性\n",
    "\n",
    "通过异常处理，我们可以控制程序在遇到问题时的行为，如尝试恢复或者安全地终止程序，这可以大大增强程序的可靠性和用户体验。\n",
    "\n",
    "### 代码示例\n",
    "\n",
    "#### 示例1：SyntaxError\n",
    "\n",
    "尝试执行以下Python代码将会导致语法错误，因为缺少了闭合的括号。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "147c5d86-7754-4edf-8588-af18fd09b07d",
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "incomplete input (491205062.py, line 2)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  Cell \u001b[1;32mIn[1], line 2\u001b[1;36m\u001b[0m\n\u001b[1;33m    print(\"Hello, world\"\u001b[0m\n\u001b[1;37m                        ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m incomplete input\n"
     ]
    }
   ],
   "source": [
    "# 语法错误示例\n",
    "print(\"Hello, world\"\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4737ba7-b699-4112-92df-6ca7f0537594",
   "metadata": {},
   "source": [
    "#### 示例2：IndentationError\n",
    "\n",
    "如果不正确地缩进，Python也会抛出错误。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7569bffa-9df0-4ed6-b31e-7c218aba7f96",
   "metadata": {},
   "outputs": [
    {
     "ename": "IndentationError",
     "evalue": "expected an indented block after function definition on line 2 (1257096022.py, line 3)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  Cell \u001b[1;32mIn[2], line 3\u001b[1;36m\u001b[0m\n\u001b[1;33m    print(\"Hello, world\")\u001b[0m\n\u001b[1;37m    ^\u001b[0m\n\u001b[1;31mIndentationError\u001b[0m\u001b[1;31m:\u001b[0m expected an indented block after function definition on line 2\n"
     ]
    }
   ],
   "source": [
    "# 缩进错误示例\n",
    "def foo():\n",
    "print(\"Hello, world\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22409003-45c3-495a-adba-d3c04b3b9c7a",
   "metadata": {},
   "source": [
    "#### 示例3：捕获异常\n",
    "\n",
    "我们可以使用`try-except`语句来捕获并处理异常，避免程序因为异常而完全停止。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "58544b5d-3a15-47e4-9eda-52ac54bdd281",
   "metadata": {},
   "outputs": [
    {
     "ename": "ZeroDivisionError",
     "evalue": "division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[4], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;241;43m10\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\n\u001b[0;32m      2\u001b[0m \u001b[38;5;28mprint\u001b[39m(result)\n",
      "\u001b[1;31mZeroDivisionError\u001b[0m: division by zero"
     ]
    }
   ],
   "source": [
    "result = 10 / 0\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "24c12c5d-a7a6-4a8e-b87e-a5bfad5cbbde",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "不能除以零！\n"
     ]
    }
   ],
   "source": [
    "# 捕获异常示例\n",
    "try:\n",
    "    # 尝试执行可能会引发异常的代码\n",
    "    result = 10 / 0\n",
    "except ZeroDivisionError:\n",
    "    # 如果发生了ZeroDivisionError异常，执行这里的代码\n",
    "    print(\"不能除以零！\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "544ca3db-b86f-4c49-8ed4-e113e54d03ee",
   "metadata": {},
   "source": [
    "在这个示例中，尝试进行了一个除以零的操作，这在数学上是未定义的，因此Python会抛出一个`ZeroDivisionError`异常。通过使用`try-except`语句，我们成功捕获了这个异常，并打印了一个友好的错误信息，而不是让程序崩溃。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc5919a1-224e-4eef-b75d-b0fa95168838",
   "metadata": {},
   "source": [
    "### 第二节：熟悉异常的类型\n",
    "\n",
    "在这一节中，我们将探索Python中几种常见的异常类型，并通过具体的代码示例来理解它们的含义和常见的触发场景。\n",
    "\n",
    "#### 常见的Python异常介绍\n",
    "\n",
    "Python有多种内置的异常类型，它们可以处理不同的错误情况。这里我们将介绍几种最常见的异常类型：\n",
    "\n",
    "- **SyntaxError（语法错误）**：代码中存在Python解释器无法识别的语法。\n",
    "- **NameError（名称错误）**：尝试访问一个Python解释器不认识的变量名。\n",
    "- **TypeError（类型错误）**：对类型进行了不支持的操作。\n",
    "- **IndexError（索引错误）**：在使用序列类型（如列表或元组）时，尝试访问一个不存在的索引。\n",
    "- **KeyError（键错误）**：尝试访问字典中不存在的键。\n",
    "- **ValueError（值错误）**：当函数接收到一个正确类型但不合适的值时抛出。\n",
    "- **ZeroDivisionError（零除错误）**：尝试将一个数字除以零。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e8179553-f6e5-4668-a0e4-9745d512c0c8",
   "metadata": {},
   "source": [
    "### 第三节：掌握try-except语句的使用\n",
    "\n",
    "#### try-except 语句的基本结构和工作原理\n",
    "\n",
    "`try-except` 语句是处理Python中异常的主要方法。它的工作原理是：尝试执行`try`块中的代码；如果代码执行过程中发生异常，Python会跳出`try`块，寻找匹配的`except`块来处理异常。\n",
    "\n",
    "基本结构如下：\n",
    "\n",
    "```\n",
    "try:\n",
    "    # 尝试执行的代码\n",
    "except ExceptionType:\n",
    "    # 处理异常的代码\n",
    "```\n",
    "\n",
    "#### 如何使用 try-except 语句捕获异常\n",
    "\n",
    "使用`try-except`语句捕获异常时，需要将可能抛出异常的代码放在`try`块中，然后根据不同的异常类型匹配相应的`except`块。\n",
    "\n",
    "**示例：捕获除零异常**\n",
    "\n",
    "```\n",
    "try:\n",
    "    result = 10 / 0\n",
    "except ZeroDivisionError:\n",
    "    print(\"发生了除零错误\")\n",
    "```\n",
    "\n",
    "在这个例子中，当尝试执行`10 / 0`时，会触发`ZeroDivisionError`，`except`块捕获到这个异常，并执行打印错误信息的操作。\n",
    "\n",
    "#### 处理多种异常的方法\n",
    "\n",
    "如果一个`try`块中的代码可能抛出多种异常，可以为每种异常类型指定一个`except`块，或者使用一个`except`块捕获多个异常。\n",
    "\n",
    "**指定多个except块：**\n",
    "\n",
    "```\n",
    "try:\n",
    "    # 一些可能抛出多种异常的代码\n",
    "    pass\n",
    "except ZeroDivisionError:\n",
    "    print(\"除零异常\")\n",
    "except ValueError:\n",
    "    print(\"值异常\")\n",
    "```\n",
    "\n",
    "**单个except块捕获多个异常：**\n",
    "\n",
    "```\n",
    "try:\n",
    "    # 一些可能抛出多种异常的代码\n",
    "    pass\n",
    "except (ZeroDivisionError, ValueError) as e:\n",
    "    print(f\"发生了异常：{e}\")\n",
    "```\n",
    "\n",
    "#### 使用异常变量获取异常信息\n",
    "\n",
    "在`except`语句中，可以通过指定一个变量来捕获异常对象，这个变量包含了异常的详细信息（比如错误消息）。这使得你可以在`except`块中访问和使用这些信息。\n",
    "\n",
    "**示例：使用异常变量**\n",
    "\n",
    "```\n",
    "try:\n",
    "    # 尝试执行的代码\n",
    "    pass\n",
    "except Exception as e:\n",
    "    print(f\"发生了异常：{e}\")\n",
    "```\n",
    "\n",
    "在这个例子中，`Exception`是一个捕获所有异常的基类。变量`e`包含了异常的详细信息，我们将它转换为字符串并打印出来。\n",
    "\n",
    "通过学习`try-except`语句的使用，可以有效地处理程序中可能发生的异常，提高程序的健壮性和用户体验。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08bf885d-a9cf-4a60-9886-a38f5fd0f851",
   "metadata": {},
   "source": [
    "### 第四节：掌握try-except-else语句的使用\n",
    "\n",
    "#### try-except-else 语句的结构\n",
    "\n",
    "`try-except-else` 语句为Python中的异常处理添加了一个`else`块，该块仅在`try`块中没有异常发生时执行。它的结构如下：\n",
    "\n",
    "```\n",
    "try:\n",
    "    # 尝试执行的代码\n",
    "except ExceptionType:\n",
    "    # 处理异常的代码\n",
    "else:\n",
    "    # 如果没有异常发生，执行的代码\n",
    "```\n",
    "\n",
    "#### 在没有异常发生时执行 else 子句\n",
    "\n",
    "`else`块的主要用途是执行那些只有在`try`块成功执行后才需要运行的代码。这可以减少在`try`块中需要放置的代码量，并帮助避免由于异常处理不当而引起的错误。\n",
    "\n",
    "**示例：使用else块**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "6ca48b9d-7149-4cfc-9874-577906210dd3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "请输入一个整数:  9.1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这不是一个整数！\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    num = int(input(\"请输入一个整数: \"))\n",
    "except ValueError:\n",
    "    print(\"这不是一个整数！\")\n",
    "else:\n",
    "    print(f\"你输入的整数是 {num}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f009024b-b007-4918-ad33-3789950bac10",
   "metadata": {},
   "source": [
    "在这个例子中，如果输入不是一个整数，将抛出`ValueError`异常，并执行`except`块中的代码。如果没有异常发生（即用户正确输入了一个整数），则执行`else`块中的代码，打印输入的整数。\n",
    "\n",
    "#### 如何合理地应用 else 子句提高代码的清晰度和可维护性\n",
    "\n",
    "使用`else`子句可以提高代码的清晰度和可维护性，因为它明确了哪部分代码是正常执行时需要运行的，与异常处理逻辑分离。它特别适合用在需要执行多步操作且只有第一步可能引发异常的场景。\n",
    "\n",
    "### 第五节：掌握try-except-finally语句的使用\n",
    "\n",
    "#### try-except-finally 语句的结构\n",
    "\n",
    "`try-except-finally` 语句引入了`finally`块，不论是否发生异常，`finally`块中的代码都将被执行。这对于资源清理和释放非常有用。其结构如下：\n",
    "\n",
    "```\n",
    "try:\n",
    "    # 尝试执行的代码\n",
    "except ExceptionType:\n",
    "    # 处理异常的代码\n",
    "finally:\n",
    "    # 无论是否发生异常，都执行的代码\n",
    "```\n",
    "\n",
    "#### finally 子句的作用和重要性\n",
    "\n",
    "`finally`子句的主要作用是执行那些必须运行的清理工作，比如关闭文件、释放锁等，确保资源正确地释放。\n",
    "\n",
    "**示例：使用finally块**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "eb895c5e-eb86-4849-8748-6f1c52a9a2bc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "编码错误\n",
      "文件已关闭\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    file = open(\"example.txt\")\n",
    "    data = file.read()\n",
    "    # 对文件进行处理\n",
    "except UnicodeDecodeError:\n",
    "    print(\"编码错误\")\n",
    "except IOError:\n",
    "    print(\"文件读取错误\")\n",
    "finally:\n",
    "    file.close()\n",
    "    print(\"文件已关闭\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39b2c89f-b6d8-481a-bc01-fe45683717b6",
   "metadata": {},
   "source": [
    "不管文件是否成功打开，或者在处理文件时是否发生异常，`finally`块中的`file.close()`都将被执行，确保文件被正确关闭。\n",
    "\n",
    "#### 在 finally 子句中添加资源释放或清理代码\n",
    "\n",
    "将资源释放或清理代码放在`finally`块中是一个好习惯，因为这保证了无论程序的执行路径如何，资源都会被正确管理和释放，从而避免资源泄露等问题。\n",
    "\n",
    "通过学习`try-except-else`和`try-except-finally`的使用，可以有效地控制程序中的异常处理流程，并确保资源的正确管理。这些知识对于编写健壮、可维护和高效的Python代码至关重要。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a429b1dc-3791-4b6c-8056-80a42bacc518",
   "metadata": {},
   "source": [
    "### 第六节：掌握raise语句的使用\n",
    "\n",
    "#### 如何使用 raise 语句抛出异常\n",
    "\n",
    "在Python中，`raise`语句用于触发一个指定的异常。可以使用它来抛出Python内置的异常，或者自定义异常。`raise`语句的基本用法如下：\n",
    "\n",
    "```\n",
    "raise ExceptionType(\"异常信息\")\n",
    "```\n",
    "\n",
    "#### 抛出内置异常和自定义异常\n",
    "\n",
    "**抛出内置异常：**\n",
    "\n",
    "```\n",
    "# 抛出一个 ValueError 异常\n",
    "raise ValueError(\"输入值有误\")\n",
    "```\n",
    "\n",
    "这个例子中，如果检测到输入值有误，程序会抛出一个`ValueError`异常，并附上一个错误信息。\n",
    "\n",
    "**自定义异常：**\n",
    "\n",
    "首先，需要定义一个继承自`Exception`的类：\n",
    "\n",
    "```\n",
    "class MyCustomError(Exception):\n",
    "    pass\n",
    "```\n",
    "\n",
    "然后，可以像抛出内置异常一样抛出这个自定义异常：\n",
    "\n",
    "```\n",
    "# 抛出自定义异常\n",
    "raise MyCustomError(\"发生了一个自定义错误\")\n",
    "```\n",
    "\n",
    "#### 使用 raise 语句进行错误处理和代码调试\n",
    "\n",
    "使用`raise`语句可以有效地对错误进行处理和代码调试。在开发过程中，当检测到某些条件不满足时，可以通过抛出异常来提醒开发者，这样有助于快速定位和修复问题。\n",
    "\n",
    "### 第七节：掌握assert断言语句的使用\n",
    "\n",
    "#### assert 语句的语法和用途\n",
    "\n",
    "`assert`语句用于断言某个条件是真的。如果条件为假，将触发一个`AssertionError`异常。`assert`语句的基本语法如下：\n",
    "\n",
    "```\n",
    "assert condition, \"条件为假时的错误信息\"\n",
    "```\n",
    "\n",
    "#### 使用 assert 语句进行条件检查\n",
    "\n",
    "`assert`语句常用于确保程序在某个特定条件下继续运行。例如，在处理用户输入或函数参数时，可以使用`assert`来验证数据的有效性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a05a69ab-686c-48e2-9498-d1e4e9137e5e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5.0\n"
     ]
    },
    {
     "ename": "AssertionError",
     "evalue": "除数不能为0",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[15], line 9\u001b[0m\n\u001b[0;32m      6\u001b[0m \u001b[38;5;28mprint\u001b[39m(divide(\u001b[38;5;241m10\u001b[39m, \u001b[38;5;241m2\u001b[39m))\n\u001b[0;32m      8\u001b[0m \u001b[38;5;66;03m# 触发 AssertionError\u001b[39;00m\n\u001b[1;32m----> 9\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mdivide\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m)\u001b[49m)\n",
      "Cell \u001b[1;32mIn[15], line 2\u001b[0m, in \u001b[0;36mdivide\u001b[1;34m(a, b)\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdivide\u001b[39m(a, b):\n\u001b[1;32m----> 2\u001b[0m     \u001b[38;5;28;01massert\u001b[39;00m b \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m0\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m除数不能为0\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m      3\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m a \u001b[38;5;241m/\u001b[39m b\n",
      "\u001b[1;31mAssertionError\u001b[0m: 除数不能为0"
     ]
    }
   ],
   "source": [
    "def divide(a, b):\n",
    "    assert b != 0, \"除数不能为0\"\n",
    "    return a / b\n",
    "\n",
    "# 正常情况\n",
    "print(divide(10, 2))\n",
    "\n",
    "# 触发 AssertionError\n",
    "print(divide(10, 0))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf832b3e-072e-4907-a989-27b78cdd04a0",
   "metadata": {},
   "source": [
    "在这个例子中，如果尝试执行除以零的操作，`assert`语句将会抛出一个`AssertionError`，并附上错误信息。\n",
    "\n",
    "#### 如何通过 assert 语句提前发现问题\n",
    "\n",
    "`assert`语句可以帮助开发者在早期发现问题，尤其是在开发和测试阶段。通过对关键条件进行断言，可以确保程序的关键部分按预期工作，或在不满足预期时立即发现。这种方式非常适合进行错误检查和自我验证，减少运行时错误。\n",
    "\n",
    "通过掌握`raise`和`assert`语句的使用，可以更加主动地控制程序的错误处理逻辑和验证程序的正确性，这对于编写可靠和健壮的Python代码是非常重要的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ffb6c505-d11d-4348-a5fb-6c23d4b046b5",
   "metadata": {},
   "source": [
    "###  第八节：掌握自定义异常的方法\n",
    "\n",
    "在Python中，除了使用内置的异常类型，你还可以创建自定义的异常来更精确地反映代码中可能出现的特定错误情况。自定义异常通过继承Python的`Exception`类或其子类来创建。\n",
    "\n",
    "#### 定义自定义异常类\n",
    "\n",
    "自定义异常类的定义通常很简单，关键是继承自`Exception`类或其任何子类。这里是一个自定义异常的基本示例：\n",
    "\n",
    "```\n",
    "class CustomError(Exception):\n",
    "    \"\"\"基础的自定义异常\"\"\"\n",
    "    pass\n",
    "```\n",
    "\n",
    "#### 继承自Exception类来创建自定义异常\n",
    "\n",
    "在自定义异常类中，你可以定义初始化方法`__init__`来接受错误消息或其他属性，使得异常信息更加丰富和具体。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "dc4ee57a-0eb6-4749-a602-1e8f569d9b34",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "错误信息: 无效输入, 错误码: 400\n"
     ]
    }
   ],
   "source": [
    "class ValidationError(Exception):\n",
    "    \"\"\"表示验证错误的自定义异常\"\"\"\n",
    "    def __init__(self, message, code):\n",
    "        super().__init__(message)\n",
    "        self.code = code\n",
    "try:\n",
    "    raise ValidationError(\"无效输入\", 400)\n",
    "except ValidationError as e:\n",
    "    print(f\"错误信息: {e}, 错误码: {e.code}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90dd8392-598b-4653-b47a-2a45cf69f0dc",
   "metadata": {},
   "source": [
    "与内置异常一样，使用`raise`语句抛出自定义异常，并通过`try-except`结构捕获这些异常。\n",
    "\n",
    "#### 自定义异常的应用场景和好处\n",
    "\n",
    "- **精确的错误处理**：通过定义自定义异常，可以创建更符合应用逻辑的错误类型，使得错误处理更加精确。\n",
    "- **改善代码可读性**：自定义异常可以使得异常处理代码更易于理解和维护，因为异常名称和错误消息可以更清楚地表达出错的原因。\n",
    "- **便于调试和维护**：在复杂的应用中，通过区分不同的错误类型，可以更容易地定位问题所在。\n",
    "\n",
    "通过学习如何自定义异常，你将能够更好地控制程序中的错误处理逻辑，创建更健壮、更易于维护和调试的Python应用。自定义异常是高级编程技巧的一部分，能够帮助你更好地组织和管理代码中的错误情况。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dceec9f3-f5d7-42c0-8e5b-fdc5f77db475",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
